/*
 * MandelbrotQuadTreeHelpers.c
 *
 *  Created on: 15.07.2012
 *      Author: 116
 */

#include <limits.h>
#include <stdlib.h>
#include <stdio.h>

#include "mxml.h"

#include "MandelbrotQuadTree.h"
#include "MandelbrotQuadTreeHelpers.h"

MandelbrotQuadTree * newRoot(double minX, double maxX, double minY, double maxY)
{
    MandelbrotQuadTree * root = malloc(sizeof(MandelbrotQuadTree));

    root->maxX = maxX;
    root->maxY = maxY;
    root->minX = minX;
    root->minY = minY;

    root->children = 0;
    root->parent = 0;
    root->positionInParent = -1;

    root->status = VOID;

    return root;
}

double getCenterX(MandelbrotQuadTree * node)
{
    return (node->minX + node->maxX) / 2.0;
}

double getCenterY(MandelbrotQuadTree * node)
{
    return (node->minY + node->maxY) / 2.0;
}

void splitNode(MandelbrotQuadTree * node)
{
    //split only if it's not already split
    if (!node->children)
    {
        node->children = malloc(sizeof(MandelbrotQuadTree) * 4);

        MandelbrotQuadTree * children = node->children;

        for (int i = 0; i < 4; ++i)
        {
            children[i].parent = node;
            children[i].children = 0;
            children[i].status = VOID;
            children[i].minIter = -1;
            children[i].maxIter = -1;

            switch (i)
            {
                case 0:
                    children[i].positionInParent = TopLeft;
                    break;

                case 1:
                    children[i].positionInParent = TopRight;
                    break;

                case 2:
                    children[i].positionInParent = BottomLeft;
                    break;

                case 3:
                    children[i].positionInParent = BottomRight;
                    break;
            }
        }

        //init bounds

        children[0].minX = node->minX;
        children[0].maxX = getCenterX(node);
        children[0].minY = getCenterY(node);
        children[0].maxY = node->maxY;

        children[1].minX = getCenterX(node);
        children[1].maxX = node->maxX;
        children[1].minY = getCenterY(node);
        children[1].maxY = node->maxY;

        children[2].minX = node->minX;
        children[2].maxX = getCenterX(node);
        children[2].minY = node->minY;
        children[2].maxY = getCenterY(node);

        children[3].minX = getCenterX(node);
        children[3].maxX = node->maxX;
        children[3].minY = node->minY;
        children[3].maxY = getCenterY(node);
    }
}

int getNodeDepth(MandelbrotQuadTree * node)
{
    if (node->parent)
    {
        return 1 + getNodeDepth(node->parent);
    }
    else
    {
        return 0;
    }
}

void buildNodePath(MandelbrotQuadTree * node, char * path)
{
    if (node->parent)
    {
        buildNodePath(node->parent, path);
        int index = getNodeDepth(node) * 2;
        path[index - 1] = '.';
        path[index] = '0' + node->positionInParent;
    }
}

char * getNodePath(MandelbrotQuadTree * node)
{
    int length = 2 * (getNodeDepth(node) + 1);
    char * string = malloc(sizeof(char) * length);
    string[0] = 'R';
    string[length - 1] = 0;
    buildNodePath(node, string);
    return string;
}

void buildShortNodePath(MandelbrotQuadTree * node, char * path)
{
    if (node->parent)
    {
        buildShortNodePath(node->parent, path);
        int index = getNodeDepth(node);
        path[index] = '0' + node->positionInParent;
    }
}

char * getShortNodePath(MandelbrotQuadTree * node)
{
    int length = (getNodeDepth(node) + 2);
    char * string = malloc(sizeof(char) * length);
    string[0] = 'R';
    string[length - 1] = 0;
    buildShortNodePath(node, string);
    return string;
}

void show(MandelbrotQuadTree * node)
{
    char * path = getNodePath(node);
    printf("Node %s\n", path);
    free(path);
    printf("Area={x:[%1.3f;%1.3f];y:[%1.3f;%1.3f]}\n", node->minX, node->maxX, node->minY, node->maxY);
    printf("Position=%i, Status=%i\n", node->positionInParent, node->status);
}

void showXML(MandelbrotQuadTree * node)
{
    if (node)
    {
        char * path = getShortNodePath(node);
        printf("<node path=\"%s\">", path);
        free(path);
        printf("<position minX=\"%f\" maxX=\"%f\" minY=\"%f\" maxY=\"%f\"/>", node->minX, node->maxX, node->minY, node->maxY);
        switch (node->status)
        {
            case VOID:
            case BROWSED:
            case INSIDE:
                printf("<info status=\"%i\"/>", node->status);
                break;
            case OUTSIDE:
                printf("<info status=\"%i\" maxIter=\"%i\" minIter=\"%i\"/>", node->status, node->maxIter, node->minIter);
                break;
        }
        if (node->children)
        {
            for (int i = 0; i < 4; ++i)
            {
                showXML(&node->children[i]);
            }
        }
        printf("</node>");
    }
}

int recursiveWrite(MandelbrotQuadTree * node, FILE * fp)
{
    if (node)
    {
        char * path = getShortNodePath(node);
        if (fprintf(fp, "<node path=\"%s\">", path) < 0)
        {
            return EXIT_FAILURE;
        }
        free(path);

        if (fprintf(fp, "<position minX=\"%f\" maxX=\"%f\" minY=\"%f\" maxY=\"%f\"/>", node->minX, node->maxX, node->minY, node->maxY) < 0)
        {
            return EXIT_FAILURE;
        }

        switch (node->status)
        {
            case VOID:
            case BROWSED:
            case INSIDE:
                if (fprintf(fp, "<info status=\"%i\"/>", node->status) < 0)
                {
                    return EXIT_FAILURE;
                }
                break;
            case OUTSIDE:
                if (fprintf(fp, "<info status=\"%i\" maxIter=\"%i\" minIter=\"%i\"/>", node->status, node->maxIter, node->minIter) < 0)
                {
                    return EXIT_FAILURE;
                }
                break;
        }
        if (node->children)
        {
            for (int i = 0; i < 4; ++i)
            {
                if (recursiveWrite(&node->children[i], fp) == EXIT_FAILURE)
                {
                    return EXIT_FAILURE;
                }
            }
        }
        if (fprintf(fp, "</node>") < 0)
        {
            return EXIT_FAILURE;
        }
    }
    else
    {
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}

void addNode(MandelbrotQuadTree * quadTreeNode, mxml_node_t * parentXmlNode)
{
    // <node path="R">
    mxml_node_t * thisXmlNode = mxmlNewElement(parentXmlNode, "node");
    mxmlElementSetAttrf(thisXmlNode, "path", "%s", getShortNodePath(quadTreeNode));

    // <position minX="-2.000000" maxX="2.000000" minY="-2.000000" maxY="2.000000" />
    mxml_node_t * position = mxmlNewElement(thisXmlNode, "position");
    mxmlElementSetAttrf(position, "minX", "%+.15E", quadTreeNode->minX);
    mxmlElementSetAttrf(position, "maxX", "%+.15E", quadTreeNode->maxX);
    mxmlElementSetAttrf(position, "minY", "%+.15E", quadTreeNode->minY);
    mxmlElementSetAttrf(position, "maxY", "%+.15E", quadTreeNode->maxY);

    // <info status="3" maxIter="2" minIter="0" />
    mxml_node_t * info = mxmlNewElement(thisXmlNode, "info");

    switch (quadTreeNode->status)
    {
        case VOID:
        case BROWSED:
        case INSIDE:
            mxmlElementSetAttrf(info, "status", "%i", quadTreeNode->status);
            break;

        case OUTSIDE:
            mxmlElementSetAttrf(info, "status", "%i", quadTreeNode->status);
            mxmlElementSetAttrf(info, "maxIter", "%i", quadTreeNode->maxIter);
            mxmlElementSetAttrf(info, "minIter", "%i", quadTreeNode->minIter);
            break;
    }

    if (quadTreeNode->children)
    {
        for (int i = 0; i < 4; ++i)
        {
            addNode(&quadTreeNode->children[i], thisXmlNode);
        }
    }
}

int writeXML(MandelbrotQuadTree * node, unsigned int pointsPerSide, unsigned int diffIterLimit, const char * name)
{
    mxml_node_t *document;
    mxml_node_t *root;

    //output :  <?xml version="1.0" encoding="UTF-8"?>
    document = mxmlNewXML("1.0");

    //example : <MandelbrotQuadTree pointsPerSide="100" diffIterLimit="3" reference="">
    root = mxmlNewElement(document, "MandelbrotQuadTree");
    mxmlElementSetAttrf(root, "pointsPerSide", "%i", pointsPerSide);
    mxmlElementSetAttrf(root, "diffIterLimit", "%i", diffIterLimit);

    addNode(node, root);

    FILE *fp;
    fp = fopen(name, "w+");

    int ret = EXIT_SUCCESS;
    if (mxmlSaveFile(document, fp, MXML_NO_CALLBACK) < 0)
    {
        ret = EXIT_FAILURE;
    }

    fclose(fp);
    return ret;
}

unsigned long getSize(MandelbrotQuadTree * node)
{
    unsigned long total = 0;

    if (node)
    {
        total += sizeof(MandelbrotQuadTree);
        if (node->children)
        {
            for (int i = 0; i < 4; ++i)
            {
                total += getSize(&node->children[i]);
            }
        }
    }

    return total;
}
